Sincronización de sistemas operativos
Supongamos que estos 3 procesos se ejecutan en paralelo, con un algoritmo de planificación RR con un quantum muy chico, y que tareasPendientes = 10. Si nosotros ponemos ejecutar el proceso 1, supongamos que llegamos a ejecutar la línea registro = registro + 1, y justo después salta el quantum, bien, pasa al proceso 2. En el proceso dos hace lo mismo pero lo resta, registro = registro – 1, y salta el quantum, vuelve al proceso 1, y posteriormente al 2 cuando nuevamente salte el quantum.
¿Cuál es el punto de mostrar este ejemplo? Que en el proceso 1, tareasPendientes puede llegar a quedar igual a 11 y en el proceso 2, igual a 9, ¿qué problema no? Veamos por qué se da este fenómeno y cómo se puede resolver.
Condición de carrera
¿Cómo tiene que ser la REGIÓN CRÍTICA?
● Debe contener alguna operación sobre un recurso compartido
● Debe ser lo más chica posible
Debe ejecutarse en forma atómica, es decir, que al momento de ejecutar una instrucción, se ejecute como UNA sola o directamente NO se ejecute, como en el caso de tareasPendientes++, que no tenga que ejecutar una línea y saltar a otra diferente, sino que el ciclo de instrucción se haga completo.
¿Cuándo puedo tener una condición de carrera?
- Cuando más de un proceso/hilo esté usando el mismo recurso
- Cuando al menos uno de los que usa ese recurso lo está modificando
- Cuando los accesos a dicho recurso se realizan en forma concurrente
Si todos los que usan dicho recurso compartido SOLO lo estuvieran LEYENDO, no se provocaría esta condición de carrera.
Sincronizar cobra sentido cuando algunos/s de los procesos que usan un recurso compartido, editan sobre dicho recurso.
Condiciones de Bernstein
Requerimientos para la solución al problema de la sección crítica
1. Mutua exclusión: Sólo un proceso puede acceder a su sección crítica a la vez
2. Progreso: Si ningún proceso está ejecutando su sección crítica y existen algunos que quieren entrar en la misma, solo los procesos que no estén ejecutando su sección restante pueden participar en la decisión de qué proceso puede ingresar en su sección crítica, y está selección no puede posponerse indefinidamente
3. Espera limitada: Tiene que haber un límite en la cantidad de veces que otros procesos pueden ingresar en sus secciones críticas luego de que un proceso pide ingresar en la suya, es decir, un proceso no debería esperar indefinidamente la autorización para ejecutar su sección crítica.
4. Velocidad de los procesos: La solución debe funcionar sin importar cómo los procesos usen sus SCs, es decir, si las mismas son largas, cortas, si las utilizan muchas o pocas veces.
Soluciones a nivel software
El tema con estas soluciones de software es que son bastantes molestas de programar, por lo que surgieron las soluciones de hardware.
Soluciones a nivel hardware
El HW nos puede dar soporte para resolver el problema de la SC en forma sencilla y eficienteEsta solución no tiene espera activa, implica cambios de modo por ser una instrucción privilegiada, y por otro lado es un bazookazo porque implica deshabilitar las interrupciones en todo el sistema.
Como vemos, la solución de deshabilitar interrupciones no es buena en sistemas de multiprocesamiento, esta solución tampoco se usa.
Una solución que puede llegar a usarse un poquito a nivel HW es la siguiente:
El problema que puede llegar a tener esta solución es que se quede clavado en el while, y que por ende tarde mucho en setear a la variable lock en FALSE. Lo que estaría bueno implementar, es que en vez de dejar esperando a los demás procesos para entrar al while, directamente los bloquee. Lo que pasa es que el hardware no es capaz de bloquearlos, cosa que sí es posible para el sistema operativo (S.O).
Soluciones de S.O a SEMÁFOROS
● Un semáforo es una variable entera que es accedida únicamente por dos funciones atómicas (syscalls) wait y signal.
● La diferencia con el resto de las estrategias vistas, es que esta se puede implementar con o sin espera activa (bloqueantes).
Semáforos: Implementación
Dato: Para el TP siempre usar pthreads_mutex_t
Tipos de semáforosLa diferencia entre el mutex y el binario, es que el primero define una región de entrada y una de salida, y el binario se usa de forma alternada para que primero ejecute un proceso y luego otro.
IMPORTANTE: SIEMPRE hay que inicializar un semáforo, pero NUNCA con un valor negativo.
En este ejercicio se resolvieron las 3 cosas más importantes que se suelen ver en los enunciados:
- Con el semáforo ‘mutex_cola’ resolvimos la parte de mutua exclusión
- Con el semáforo contador ‘mensaje_insertado’ resolvimos la parte de orden o la parte de avisarle algo a otro proceso
- Con el semáforo ‘lugar_en_cola’ resolvimos la parte del tema del lugar, que en general es N o algún valor específico
Inversión de prioridades
Monitores
La idea de los monitores es la siguiente: supongamos que tenemos una variable global a la que acceden varios procesos y la modifican, ¿qué debe hacer cada proceso para que se asegure la mutua exclusión? usar un mutex. Hasta ahí vamos bien, pero… ¿existirá alguna forma de ahorrarnos tener que hacer wait y signal cada vez que el proceso quiera modificar dicha variable?
La respuesta es SÍ, y se llaman monitores.